#include "Timer.hpp"
#include "BaseLog.hpp"
#include "Defind.hpp"

#include <functional>
#include <cstdlib>
#include <cstdio>
#include <cstring>


using namespace DBServer::Base::Timer;

/**
 * 
 * 
*/
Timer_n::Timer_n(std::function<void(void)> _cb)
    : cb(_cb)
{
    clock = Time();
    prev = next = nullptr;
}

/* timing list */
TimingList::TimingList() 
    :_size(0)
{
    // virtual head and tail
    auto f = []{};
    head = new Timer_n(f);
    tail = new Timer_n(f);

    head->next = tail;
    head->prev = tail->next = nullptr;
    tail->prev = head;
}

TimingList::~TimingList() 
{
    auto iter = head->next;
    while (iter)
    {
        auto tmp = iter;
        iter = iter->next;
        delete head;    
    }
    delete head;
    delete tail;
}

auto TimingList::add(Timer_n& t) -> int
{
    return push(t);
}

auto TimingList::del(Timer_n& t) -> int
{
    return erase(&t);
}

auto TimingList::mod(Timer_n& t) -> int
{
    if (!erase(&t))
        return -1;
    return push(t);
}

auto TimingList::size() -> unsigned
{
    return _size;
}

auto TimingList::empty() -> bool
{
    return _size == 0;
}

auto TimingList::is_timeout() -> bool
{
    return head->next->clock.is_timeout();
}

auto TimingList::push(Timer_n& t) -> int
{
    auto iter = head;
    if (_size == 0)
    {
        head->next = &t;
        tail->prev = &t;
        t.prev = head;
        t.next = tail;
        goto ret;
    }
    
    while (iter != tail)
    {
        if (t.clock <= iter->clock)
            break;
        iter = iter->next;
    }
    t.next = iter;
    t.prev = iter->prev;
    iter->prev = &t;
    t.prev->next = &t;
ret:
    ++_size;
    return 0;
}

auto TimingList::pop(Timer_n& t) -> bool
{
    if (_size == 0) {
        warn("The list is empty!\n");
        exit(-1);
    }
    auto node = head->next;
    node->prev->next = node->next;
    node->next->prev = node->prev;
    delete node;
    --_size;
    return true;
}

auto TimingList::top() -> Timer_n&
{
    if (_size == 0) {
        warn("The list is empty!\n");
        exit(-1);
    }
    return *(head->next);
}

auto TimingList::erase(Timer_n* t) -> bool
{
    auto iter = head;
    while (iter != tail)
        if (iter == t) break;
        else iter = iter->next;

    if (iter == tail) return false;
    iter->prev->next = t->next;
    t->next->prev = iter->prev;
    --_size;

    return true;
}


/**
 * 
 * 
*/

Timer_w::Timer_w()
    : slot(0), circle(0), next(nullptr), prev(nullptr)
{}

Timer_w::Timer_w(const Timer_w& timer)
{
    this->circle = timer.circle;
    this->slot = timer.slot;
    this->prev = this->next = nullptr;
}

auto Timer_w::operator=(const Timer_w& timer_) -> const Timer_w
{
    Timer_w timer;
    timer.circle = timer_.circle;
    timer.slot = timer_.slot;
    timer.next = timer.prev = nullptr;
    return static_cast<const Timer_w>(timer);
}

TimingWheel::TimingWheel(time_tt _tick, int _slots)
    : cur(0), tick(_tick), slots(_slots)
{
    // calculate circle time of the time wheel 
    // by time wheel slots and time tick
    this->circle_time =  slots * tick;
    wheel = new Timer_w*[slots];
}

TimingWheel::~TimingWheel()
{
    // traverse the time wheel to free all timer
    Timer_w *iter, *temp;
    for (int i = 0; i < slots; ++i)
    {
        iter = wheel[i];
        while ( iter != nullptr )
        {
            temp = iter;
            iter = iter->next;
            delete temp;
        }
    }
    // clean the wheel
    delete wheel;
}

auto TimingWheel::add(Timer_w &timer) -> void
{
    _add_and_mod(&timer);
}

auto TimingWheel::add(std::function<void(void)> _cb, time_tt t) -> const Timer_w&
{
    // create a timer
    Timer_w *timer = new Timer_w;
    // initalize the number
    set_cb(timer, _cb);
    set_circle(timer, t);
    set_slot(timer, t);

    // add to the wheel
    _add_and_mod(timer);
    return *timer;
}

auto TimingWheel::mod(Timer_w& timer, time_tt t, std::function<void(void)> *cb = nullptr) -> bool
{
    if (!_del(&timer))
        return false;

    if (cb != nullptr)
        set_cb(&timer, *cb);
    
    set_circle(&timer, t);
    set_slot(&timer, t);

    _add_and_mod(&timer);
    
    return true;
}

auto TimingWheel::del(Timer_w& timer) -> bool
{
    return _del(&timer);
}

auto TimingWheel::set_cb(Timer_w *timer, std::function<void(void)> cb) -> void
{
    timer->cb = cb;
}

auto TimingWheel::set_circle(Timer_w *timer, time_tt t) -> void
{
    timer->circle = t % circle_time;
}

auto TimingWheel::set_slot(Timer_w *timer, time_tt t) -> void
{
    timer->slot = t / circle_time;
}

auto TimingWheel::_add_and_mod(Timer_w *timer) -> void
{
    auto iter = wheel[timer->slot];
    if (iter == nullptr) 
    {
        wheel[timer->slot] = timer;
    }
    else if (timer->circle < iter->circle)
    {
        timer->next = iter;
        iter->prev = timer;
        wheel[timer->slot] = timer;
    }
    else
    {
        while (iter->circle < timer->circle)
        {
            iter = iter->next;
        }
        if (iter == timer) return;
        timer->next = iter;
        iter->prev->next = timer;
        timer->prev = iter->prev;
        iter->prev = iter;
    }
}

auto TimingWheel::_del(Timer_w *timer) -> bool
{
    auto iter = wheel[timer->slot];
    if (iter == timer) 
    {
        wheel[timer->slot] = iter->next;
        iter->next = nullptr;
    }
    else
    {
        while (iter && iter != timer) 
            iter = iter->next;

        if (iter != timer) 
            return false;
        else
        {
            iter->prev->next = iter->next;
            iter->next->prev = iter->prev;
            iter->next = iter->prev = nullptr;
        }
    }
    return true;
    
}

/**
 * 
 * 
*/

/* Timer_h */
Timer_h::Timer_h(std::function<void(void)>& _cb)
    : cb(_cb), hole_index(-1)
{
    clock = Time();
}

/* TimingHeap */
TimingHeap::TimingHeap()
    : total(HEAP_SIZE), _size(0)
{
    heap = new Timer_h*[HEAP_SIZE];
}

TimingHeap::~TimingHeap()
{
    for (int i = 0; i < total; i++)
        delete heap[i];
    delete heap;
}

auto TimingHeap::add(Timer_h& _timer) -> int
{
    return push(_timer);
}

auto TimingHeap::mod(Timer_h& _timer) -> int
{
    return (erase(&_timer) && push(_timer));
}

auto TimingHeap::del(Timer_h& _timer) -> int
{
    return erase(&_timer);
}

auto TimingHeap::is_timeout() -> bool
{
    return heap[0]->clock.is_timeout();
}

/* api */
auto TimingHeap::push(Timer_h& timer) -> int
{
    if (_size == total) dilatation();
    shift_down(&timer, _size);
    return 0;
}

auto TimingHeap::top() -> Timer_h&
{
    if (empty()) {
        // log
        warn("the heap is empty\n");
        exit(1);
    }
    return *heap[0];
}

auto TimingHeap::pop() -> bool
{
    if (empty()) return false;
    shift_up(0);
    return true;
}

auto TimingHeap::erase(Timer_h* timer) -> int
{
    if (timer->hole_index == -1) 
        return -1;
    
    shift_up(timer->hole_index);
    return 0;
}

// size + 1 at a time
auto TimingHeap::shift_down(Timer_h* timer, unsigned hole_index) -> void
{
    unsigned parent = (hole_index - 1) / 2;

    while (hole_index && (heap[parent]->clock > timer->clock))
    {
        heap[parent]->hole_index = hole_index;
        heap[hole_index] = heap[parent];
        hole_index = parent;
        parent = (hole_index - 1) / 2;
    }
    heap[hole_index] = timer;
    timer->hole_index = hole_index;
    ++_size;
}

// size -1 at a time
auto TimingHeap::shift_up(unsigned hole_index) -> void
{
    // right chile
    unsigned min_chile = 2 * (hole_index + 1);

    while (min_chile <= _size)
    {
        min_chile -= (min_chile == _size ||
                      (heap[min_chile]->clock > heap[min_chile-1]->clock));
        
        heap[min_chile]->hole_index = hole_index;
        heap[hole_index] = heap[min_chile];
        hole_index = min_chile;
        min_chile = 2 * (hole_index + 1);
    }

    if (hole_index != _size-1) heap[hole_index] = heap[_size-1];
    --_size;
}

auto TimingHeap::empty() -> bool
{
    return _size == 0;
}
    
auto TimingHeap::size() -> unsigned
{
    return _size;
}

auto TimingHeap::dilatation() -> void
{
    const unsigned new_size = total * HEAP_DILATATION;
    auto new_heap = new Timer_h*[new_size];
    memset(new_heap, 0, sizeof(Timer_h*)*new_size);
    memcpy(new_heap, heap, sizeof(Timer_h*)*_size);

    delete heap;
    heap = new_heap;
    total = new_size;
}